#!/usr/bin/env python
# coding: utf-8

# # CNKI_Selenium

# In[1]:


import pandas as pd
import numpy as np
from lxml.html import fromstring
import time
from random import random
import requests
import base64


# In[2]:


# 指定路径为下面的下载文件做准备
from selenium import webdriver
options = webdriver.ChromeOptions()
out_path = r'C:\Users\46547\Desktop\web_ming_final'  # 是你想指定的路径
prefs = {'profile.default_content_settings.popups': 0, 'download.default_directory': out_path}
options.add_experimental_option('prefs', prefs)
browser = webdriver.Chrome(executable_path=r'C:\Users\46547\AppData\Local\Google\Chrome\Application\chromedriver.exe', chrome_options=options)


# In[3]:


from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

opts = webdriver.ChromeOptions()
opts.add_argument('-no-sandbox')#解决DevToolsActivePort文件不存在的报错
opts.add_argument('window-size=1920x3000')#指定浏览器分辨率
opts.add_argument('--disable-gpu')#谷歌文档提到需要加上→这个属性来规避bug
opts.add_argument('--hide-scrollbars')#隐藏滚动条，应对特殊页面


driver = webdriver.Chrome(chrome_options = opts)#desired_capabilities=caps


# * 如果使用校内网，直接登录[中国知网](https://www.cnki.net/)
# * 如果使用校外网，请登录[外部访问系统](http://fsso.cnki.net/)

# # 检查是否登录

# In[4]:


driver.get("https://www.cnki.net/")


# In[5]:


# driver.find_element_by_xpath('//*[@id="headerBox"]/div[1]/div/div/div[4]').click()


# ## 校内网_直接登录

# In[6]:


# #校园网ip登录
# element = driver.find_element_by_id('Button2')
# element.click()


# In[7]:


element = driver.find_element_by_id('Ecp_loginShowName1')
element.get_attribute('innerHTML')


# # 检查窗口位置

# * 出现多个窗口，检查窗口位置
# * 每一个窗口在driver中自动生成唯一的窗口id

# In[8]:


# 点击高级检索
element = driver.find_elements_by_xpath('//div[@class="readvce"]/a')[0]
element.click()


# In[9]:


#查看窗口信息（现在打开了两个窗口）
driver.window_handles


# In[10]:


driver.current_window_handle


# In[11]:


driver.switch_to_window(driver.window_handles[1])


# ## 直接搜索栏添加索引词

# In[11]:


search_index =  {"theme": "智慧物联网", "author": "","literature":""}


# In[12]:


## 清空主题input
driver.find_element_by_xpath('//*[@id="gradetxt"]/dd[1]/div[2]/input').clear()


# In[13]:


driver.find_element_by_xpath('//*[@id="gradetxt"]/dd[1]/div[2]/input').send_keys(search_index['theme'])


# In[14]:


## 清空作者input
driver.find_element_by_xpath('//*[@id="gradetxt"]/dd[2]/div[2]/input').clear()


# In[15]:


driver.find_element_by_xpath('//*[@id="gradetxt"]/dd[2]/div[2]/input').send_keys(search_index['author'])


# In[16]:


## 清空文献来源input
driver.find_element_by_xpath('//*[@id="gradetxt"]/dd[3]/div[2]/input').clear()


# In[17]:


driver.find_element_by_xpath('//*[@id="gradetxt"]/dd[3]/div[2]/input').send_keys(search_index['literature'])


# In[20]:


element = driver.find_element_by_xpath('//input[@value="检索"]')
element.click()


# # 点击期刊检索以及选择期刊

# In[11]:


# 点击期刊
driver.find_element_by_xpath('//ul[@class="doctype-menus keji"]/li/a').click()


# In[12]:


#点击CSSI
driver.find_element_by_xpath('//input[@key="CSI"]').click()


# In[13]:


#点击北大核心
driver.find_element_by_xpath('//input[@key="HX"]').click()


# In[14]:


driver.find_element_by_xpath('/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/div[2]/input').click()


# # 填写query
# * 可以在高级检索直接检索（只要不精确查找）
# * 建议专业检索

# In[12]:


# 点击期刊
driver.find_element_by_xpath('//ul[@class="doctype-menus keji"]/li/a').click()


# In[13]:


# 点击专业检索
driver.find_element_by_name('majorSearch').click()


# In[14]:


AI_新媒体_query = '(TI="物联网" and SU="智能") OR (TI="AI" and SU = "科学")'


# In[15]:


element = driver.find_element_by_xpath('//textarea')
element.clear()
element.send_keys(AI_新媒体_query)


# In[16]:


# driver.find_element_by_xpath('/html/body/div[4]/div/div[2]/div/div[1]/div[2]/dl/dd[1]/p').get_attribute('innerHTML')


# In[17]:


#点击CSSI
driver.find_element_by_xpath('//input[@key="CSI"]').click()


# In[18]:


#点击北大核心
driver.find_element_by_xpath('//input[@key="HX"]').click()


# In[19]:


driver.find_element_by_xpath('/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/div[2]/input').click()


# # 点击检索(点击页面显示50篇+全选)

# In[20]:


# 显示
element = driver.find_element_by_xpath('//*[@id="perPageDiv"]/div')
element.click()


# In[21]:


# 50 
element = driver.find_element_by_xpath('//*[@id="perPageDiv"]/ul/li[3]')
element.click()


# # 获取页面内容

# In[22]:


element = driver.find_element_by_id('gridTable')
page_html = element.get_attribute('innerHTML')
data = pd.read_html(page_html)[0]
data


# # 翻页

# In[23]:


import time


# In[24]:


# 翻页
element = driver.find_element_by_id('PageNext')
element.get_attribute('innerHTML')


# In[25]:


# 跳转上限
element = driver.find_element_by_xpath('//span[@class="countPageMark"]')
page_str = element.get_attribute('innerHTML')
page_str 


# In[26]:


page_int = page_str.split('/')
page_int


# In[27]:


pages = list(range(1,int(page_int[1])+1))
print(pages)


# In[32]:


# 取27页
pages = list(range(1,28))


# In[33]:


表格_html = dict()
main_content =""
element = None


# In[34]:


def process_pages (pages):
    for p in pages:
        print (p,end='\t')
        跳转 = driver.find_element_by_id('PageNext')
        跳转.click()
        time.sleep(8+1*random())
        # 获取含有页面主要数据的表格
        element = driver.find_element_by_id('gridTable')
        main_content = element.get_attribute('innerHTML')
        表格_html[p] = main_content


# In[37]:


process_pages(pages)


# In[38]:


df = pd.DataFrame([表格_html]).T
df.columns = ["html_snippets"]
df


# In[39]:


网站 = "中国知网"
fn = { "output" : { "htm_snippets": "data_raw_src/知网_htm_snippets_{网站}.tsv"}
     }


# In[40]:


# 保存页面内容的csv文件
filename = fn ["output"] ["htm_snippets"] 
df.to_csv(filename.format(网站=网站), sep="\t", encoding="utf8")


# In[41]:


l_df = []
for p in pages:
    表格 = pd.read_html(表格_html[p])[0]
    l_df.append(表格)


# In[42]:


df_url_out = pd.concat(l_df).reset_index(drop=True)
df_总表 = data.append(df_url_out)
df_总表


# In[43]:


# 将内容表格存在本地
with pd.ExcelWriter('知网文章数据.xlsx',mode='w',engine="openpyxl") as writer:  
            df_总表.to_excel(writer,sheet_name="知网")
display(df_总表)


# # 下载文件以及原文（refworks/pdf/text）

# ## 开始第一轮下载

# In[44]:


# 导出refworks文件（.txt）和下载文章
# 每次全选不能超过500篇，分3次进行

pages = list(range(1,11))
print(pages)


# In[45]:


# 返回第一页
driver.find_element_by_id('total').click()


# In[46]:


# 清除选中
driver.find_element_by_xpath('//*[@id="gridTable"]/div[1]/div[2]/div[1]/a').click()


# In[47]:


# 选中页面50篇 —> 翻页
def process_choose(pages):
    for p in pages:
        print (p,end='\t')
        全选 = driver.find_element_by_id('selectCheckAll1')
        全选.click()
        time.sleep(8+1*random())
        跳转 = driver.find_element_by_id('PageNext')
        跳转.click()
        time.sleep(5)


# In[48]:


process_choose(pages)


# In[49]:


# # 因出现错误（非验证码），补选最后两页文章
# driver.find_element_by_id('selectCheckAll1').click()


# In[50]:


# driver.find_element_by_id('PageNext').click()


# In[51]:


# driver.find_element_by_id('selectCheckAll1').click()


# In[52]:


# 导出与分析 
driver.find_element_by_xpath('//i[@class="icon-d"]').click()


# In[53]:


# 导出文献
driver.find_element_by_xpath('//i[@class="icon-r"]').click()


# In[54]:


# 点击Refworks
driver.find_element_by_xpath('//a[@exporttype="Refworks"]').click()


# In[79]:


# 所有窗口ID
driver.window_handles


# In[57]:


# 导出 .txt文件
driver.find_element_by_xpath('//i[@class="icon icon-export"]').click()


# In[80]:


# 关闭已经操作过的窗口
driver.close()


# In[87]:


# 窗口切换
driver.switch_to_window(driver.window_handles[1])


# In[88]:


# 批量下载
driver.find_element_by_xpath('//li[@class="bulkdownload export"]').click()


# In[89]:


# 窗口切换
driver.switch_to_window(driver.window_handles[2])


# In[90]:


# 下载所选文献（500篇）
driver.find_element_by_id('btn-download-all').click()


# In[91]:


# 关闭已经操作过的窗口
driver.close()


# ## 开始第二轮下载

# In[92]:


# 窗口切换
driver.switch_to_window(driver.window_handles[1])


# In[93]:


# 清除选择
driver.find_element_by_xpath('//*[@id="gridTable"]/div[1]/div[2]/div[1]/a').click()


# In[94]:


driver.find_element_by_id('PageNext').click()


# In[95]:


# 第二轮下载
pages = list(range(11,21))
print(pages)


# In[96]:


process_choose(pages)


# In[105]:


# 导出与分析 
driver.find_element_by_xpath('//i[@class="icon-d"]').click()


# In[106]:


# 导出文献
driver.find_element_by_xpath('//i[@class="icon-r"]').click()


# In[107]:


# 点击Refworks
driver.find_element_by_xpath('//a[@exporttype="Refworks"]').click()


# In[100]:


# 所有窗口ID
driver.window_handles


# In[108]:


# 窗口切换
driver.switch_to_window(driver.window_handles[2])


# In[109]:


# 导出 .txt文件
driver.find_element_by_xpath('//i[@class="icon icon-export"]').click()


# In[110]:


# 窗口切换
driver.switch_to_window(driver.window_handles[2])


# In[111]:


# 关闭已经操作过的窗口
driver.close()


# In[112]:


# 窗口切换
driver.switch_to_window(driver.window_handles[1])


# In[113]:


# 批量下载
driver.find_element_by_xpath('//li[@class="bulkdownload export"]').click()


# In[114]:


# 窗口切换
driver.switch_to_window(driver.window_handles[2])


# In[115]:


# 下载所选文献（500篇）
driver.find_element_by_id('btn-download-all').click()


# In[116]:


# 关闭已经操作过的窗口
driver.close()


# ## 开始第三轮下载

# In[117]:


# 窗口切换
driver.switch_to_window(driver.window_handles[1])


# In[118]:


# 清除选择
driver.find_element_by_xpath('//*[@id="gridTable"]/div[1]/div[2]/div[1]/a').click()


# In[119]:


driver.find_element_by_id('PageNext').click()


# In[124]:


# 第三轮下载
pages = list(range(21,28))
print(pages)


# In[126]:


process_choose(pages)


# In[129]:


# 选上最后一页
driver.find_element_by_id('selectCheckAll1').click()


# In[130]:


# 导出与分析 
driver.find_element_by_xpath('//i[@class="icon-d"]').click()


# In[131]:


# 导出文献
driver.find_element_by_xpath('//i[@class="icon-r"]').click()


# In[132]:


# 点击Refworks
driver.find_element_by_xpath('//a[@exporttype="Refworks"]').click()


# In[133]:


# 所有窗口ID
driver.window_handles


# In[134]:


# 窗口切换
driver.switch_to_window(driver.window_handles[2])


# In[135]:


# 导出 .txt文件
driver.find_element_by_xpath('//i[@class="icon icon-export"]').click()


# In[141]:


# 关闭已经操作过的窗口
driver.close()


# In[142]:


# 窗口切换
driver.switch_to_window(driver.window_handles[1])


# In[143]:


# 批量下载
driver.find_element_by_xpath('//li[@class="bulkdownload export"]').click()


# In[144]:


# 窗口切换
driver.switch_to_window(driver.window_handles[2])


# In[145]:


# 下载所选文献（最后篇幅）
driver.find_element_by_id('btn-download-all').click()


# # 解决验证码问题：下载pdf

# ## 解决必要时候的下载文章的验证码问题

# In[104]:


# 截图验证码并且保存到本地准备用api解决验证码问题
# coding:utf-8
from selenium import webdriver
from PIL import Image
##截图
driver.save_screenshot (r"C:\Users\46547\Desktop\web_ming_final\image\下载.png")
#2.定位到验证码图片元素
code_ele =driver. find_element_by_xpath ('//*[@id="changeVercode"]')
#3.元素的位置，结果：P'y';,x:},为图片左上角的位置
print(code_ele.location)
#4.元素的大小，结果：｛＇height';,'width':}
print(code_ele.size)
#5.得到将元素的具体位置
x0 = code_ele.location["x"]+250
y0=code_ele.location["y"]
x1 = code_ele.size["width"] + x0+2000
y1=code_ele.size["height"] +y0 +3000
img = Image.open(open (r"C:\Users\46547\Desktop\web_ming_final\image\下载.png",'rb'))
image = img.crop((x0,y0,x1,y1))#左、上、右、下
image.save(r"C:\Users\46547\Desktop\web_ming_final\image\验证码＿下载.png")#将验证码图片保存为code_img.png


# In[105]:


# 下载文章验证码进行api识别
import requests
import time
import hashlib
import base64
import json
def parse_img(APPID,API_KEY):
    curTime = str(int(time.time()))
#  支持语言类型和是否开启位置定位(默认否)
    param = {"language": "cn|en", "location": "false"}
    param = json.dumps(param)
    paramBase64 = base64.b64encode(param.encode('utf-8'))

    m2 = hashlib.md5()
    str1 = API_KEY + curTime + str(paramBase64,'utf-8')
    m2.update(str1.encode('utf-8'))
    checkSum = m2.hexdigest()

    # 组装http请求头
    header = {
        'X-CurTime': curTime,
        'X-Param': paramBase64,
        'X-Appid': APPID,
        'X-CheckSum': checkSum,
        'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',}
    URL = "http://webapi.xfyun.cn/v1/service/v1/ocr/general"
    with open(r'C:\Users\46547\Desktop\web_ming_final\image\验证码＿下载.png','rb') as f:
            img = f.read()
            img = str(base64.b64encode(img), 'utf-8')
    data = {'image': img}
    r = requests.post(URL, data=data, headers=header)
    ret = str(r.content, 'utf-8')
    result = json.loads(ret)['data']['block'][0]['line'][0]['word'][0]['content']
    return result


# In[110]:


parse_img("427b1be6","ba65f1334305e5cb63a4da6d3f2071f8")


# In[111]:


线框验证码=driver.find_element_by_xpath('//*[@id="vcode"]')
线框验证码.send_keys(result)
driver.find_element_by_xpath('/html/body/div/form/dl/dd/button').click()


# ## 将验证码识别过程封装成函数

# ***需将APPID、API_KEY以及屏幕设配做出个人相应调整***  
# ***创建img文件夹、调整浏览器下载的路径，在下面代码做出相应的调整，否则无法使用***

# In[29]:


def verification_code():
        ###循环10次一般可以解决验证码，不够可再加
        for i in range(0,10):
            ##截图（创建文件夹用于保存图片）
            driver.save_screenshot (r"C:\Users\46547\Desktop\web_ming_final\下载.png")
            #2.定位到验证码图片元素(调整位置)
            code_ele =driver.find_element_by_xpath ('//*[@id="vImg"]')
            x0 = code_ele.location["x"] +120
            y0=code_ele.location["y"] +70
            x1 = code_ele.size["width"] + x0 + 120
            y1=code_ele.size["height"] +y0
            img = Image.open(open (r"C:\Users\46547\Desktop\web_ming_final\下载.png",'rb'))
            image = img.crop((x0,y0,x1,y1))#左、上、右、下
            image.save(r"C:\Users\46547\Desktop\web_ming_final\验证码＿下载.png")#将验证码图片保存为code_img.png为code_img.png
        #调用api
            sleep(1)
            ####APPID="427b1be6"
            ####API_KEY="ba65f1334305e5cb63a4da6d3f2071f8"
            curTime = str(int(time.time()))
        #  支持语言类型和是否开启位置定位(默认否)
            param = {"language": "cn|en", "location": "false"}
            param = json.dumps(param)
            paramBase64 = base64.b64encode(param.encode('utf-8'))

            m2 = hashlib.md5()
            str1 = API_KEY + curTime + str(paramBase64,'utf-8')
            m2.update(str1.encode('utf-8'))
            checkSum = m2.hexdigest()

        # 组装http请求头
            header = {
                'X-CurTime': curTime,
                'X-Param': paramBase64,
                'X-Appid': APPID,
                'X-CheckSum': checkSum,
                'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',}
            URL = "http://webapi.xfyun.cn/v1/service/v1/ocr/general"
            with open(r'C:\Users\46547\Desktop\web_ming_final\验证码＿下载.png','rb') as f:
                img = f.read()
                img = str(base64.b64encode(img), 'utf-8')
                data = {'image': img}
            r = requests.post(URL, data=data, headers=header)
            ret = str(r.content, 'utf-8')
            ### 判断api输入的值是否符合条件（可能只有3位数，导致错误）
            try:
                result=json.loads(ret)['data']['block'][0]['line'][0]['word'][0]['content']
                线框验证码=driver.find_element_by_xpath('//*[@id="vcode"]')
                线框验证码.clear()
                线框验证码.send_keys(result)
                driver.find_element_by_xpath('/html/body/div/form/dl/dd/button').click()
                sleep(2)
                ###失败则更换图片继续运行
                try:
                    driver.find_element_by_xpath('//*[@id="vImg"]').click()
                    driver.find_element_by_xpath('/html/body/div/form/dl/dd/button').click()
                    driver.find_element_by_xpath ('//*[@id="vImg"]') 
                ###成功或出现并发、错误则关闭下载页和详情页返回总链接页结束
                except:
                    driver.close()
                    sleep(2)
                    driver.switch_to_window(driver.window_handles[2])
                    sleep(2)
                    driver.close()
                    driver.switch_to_window(driver.window_handles[1])
                    break###成功则下载，不成功则pass（下一个）
             ### 不符合则点击换验证码      
            except:
                driver.find_element_by_xpath('//*[@id="vImg"]').click()
            return


# ## 验证码问题最终封装函数代码

# In[147]:


driver.switch_to_window(driver.window_handles[1])
element = driver.find_element_by_xpath('//span[@class="total"]')
max_page=int(element.get_attribute("textContent").replace("共","").replace("页",""))
for a in range(1,max_page):
    ###点击详情页下载
    all_当前页面的所有文章=driver.find_elements_by_xpath('//td[@class="name"]//a')
    sleep(3)
    for b in range(len(all_当前页面的所有文章)):
        driver.find_elements_by_xpath('//td[@class="name"]//a')[b].click()
        driver.switch_to_window(driver.window_handles[2])
        path = "D:\知网_pdf"      # 输入文件夹地址（更改为自己浏览器下载文件夹）
        files = os.listdir(path)   # 读入文件夹
        num = len(files) 
        element=driver.find_element_by_xpath('//*[@id="pdfDown"]')
        driver.execute_script("arguments[0].click();", element)
        sleep(6)
        nums= len(os.listdir(path))
        ### 查看是否有新增下载文件出现    
        if nums>num:
            driver.close()
            sleep(1)
            driver.switch_to_window(driver.window_handles[1])
        ### 无则进行是否有验证码页面的判定
        else:
            driver.switch_to_window(driver.window_handles[-1])
            driver.switch_to_window(driver.window_handles[-2])
            driver.switch_to_window(driver.window_handles[-1])
            try:
                driver.find_element_by_xpath ('//*[@id="vImg"]')
                verification_code()
            ### 没出现验证码则关闭当前窗口退回
            except:
                driver.close()
                driver.switch_to_window(driver.window_handles[1])###回到链接页面
    element=driver.find_element_by_xpath('//*[@id="PageNext"]')
    element.click()


# # 期末总结

# ## 本次期末代码中间遇到的问题：
# 1. 每次文章批量下载只能下载500篇；
# 2. 随着下载次数和频率的增加，大概率会出现验证码问题；
# 3. 后期窗口切换较多，需要适当关闭部分已经操作过的窗口；

# ## 解决办法：
# 1. 将1,354篇文章分开3次进行下载
# 2. 加入api，解决验证码问题
# 3. 后期窗口较多，适时关闭某些窗口
